use crate::{
    lexer::{self, token::Token, LexerBuilder, Observe},
    Error, Result,
};
use gloo::{console::log, utils::document, utils::window};
use std::{
    collections::HashMap,
    fmt::{Debug, Formatter},
    ops::Range,
};
use wasm_bindgen::JsCast;
use web_sys::{HtmlElement, Node, Selection};
use yew::prelude::*;

#[derive(Debug, Clone)]
pub struct CompilerEditor {
    // 按照顺序排序的tokens，不用保存last index
    tokens: Vec<NodeToken>,
    root_node: NodeRef,
    cursor: Option<u32>,
    node_ref: NodeRef,
    keywords: HashMap<String, Token>, // 哪些是关键字
}

pub enum Msg {
    Key(String),
}

#[derive(Debug, Clone, Properties)]
pub struct Props {
    pub keywords: HashMap<String, Token>,
}

impl PartialEq for Props {
    fn eq(&self, other: &Self) -> bool {
        if self.keywords.len() != other.keywords.len() {
            return false;
        }

        for (k, v) in &self.keywords {
            let other_v = other.keywords.get(k);
            if let Some(oth_v) = other_v {
                if oth_v != v {
                    return false;
                }
            } else {
                return false;
            }
        }
        true
    }
}
/// 实现组件
impl Component for CompilerEditor {
    type Message = Msg;
    type Properties = Props;

    fn create(ctx: &Context<Self>) -> Self {
        Self {
            tokens: vec![],
            root_node: Default::default(),
            cursor: None,
            node_ref: NodeRef::default(),
            keywords: ctx.props().keywords.clone(),
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::Key(key) => {
                let ret = self.keyup(&key);
                log!(format!("ret: {:?}", ret));
            }
        }
        false
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let onkeyup_callback = ctx.link().callback(|e: KeyboardEvent| Msg::Key(e.key()));
        html! {
            <div  onkeyup={onkeyup_callback} style="overflow-y:auto; height: 480px; border: 1px solid black; padding-left:4px; padding-right: 4px" contenteditable="true"
             ref={self.node_ref.clone()}>
            </div>
        }
    }
}

impl CompilerEditor {
    fn change_node(this: Edit, ctx: Node) {
        let div_node = ctx;
        let text_node = div_node.child_nodes().get(0);
        if text_node.is_none() {
            return;
        }
        let text_node = text_node.unwrap();
        if let Some(code) = &text_node.node_value() {
            let code = Self::change_nbsp_to_space(code);
            log!(format!("change_node: {} len:{}", code, code.len()));
            let mut lexer_new = LexerBuilder::new()
                .set_line_count(1)
                .set_source_code(&code)
                .set_observer(this, div_node)
                .build();
            let _ = lexer_new.lexing();
        }
    }

    fn high_light_syntax(&mut self) -> Result<()> {
        if self.tokens.len() == 0 {
            return Ok(());
        }
        let line_div = &self.tokens[0].div_node;
        let text_node = line_div.child_nodes().get(0).ok_or(Error::from(
            "high_light_syntax： line_div.child_nodes() at less 1",
        ))?;

        // 词法解析器的流程，遇到不能解析的字符，直接返回错误了。这时候返回了不完全的token队列,
        // 导致后面的字符串丢失掉了
        let str_len = Self::text_string(&text_node).len();
        if self.tokens.len() == 0 && str_len != 0 {
            return Ok(());
        }
        for index in 0..self.tokens.len() {
            let token_node = &self.tokens[index];
            let token = &token_node.token;
            if index == 0usize {
                self.high_light_word(token, &text_node, 0..token.begin());
                continue;
            }
            let last_end = self.tokens[index - 1_usize].token.end();
            let begin = token.begin();
            let r = if last_end + 1_usize >= begin {
                // 什么都不做
                0..0
            } else {
                last_end + 1_usize..begin
            };
            self.high_light_word(token, &text_node, r);
        }

        // 找出最后一个token
        let last_token = &self.tokens[self.tokens.len() - 1];
        log!(format!(
            "str: {} len: {}",
            text_node.node_value().unwrap(),
            str_len
        ));
        if !lexer::is_eof(&last_token.token) {
            Self::insert_text_string(&text_node, last_token.token.end() + 1..str_len);
        }
        line_div
            .remove_child(&text_node)
            .map_err(|e| Error::from(format!("{:?}", e)))?;
        self.tokens = vec![];
        Ok(())
    }

    fn high_light_word(&self, token: &Token, text_node: &Node, range: Range<usize>) {
        Self::insert_text_string(text_node, range);
        let parent_node = text_node.parent_node().unwrap();
        if lexer::is_eof(token) {
            return;
        }
        let color = if lexer::is_identifier(token) {
            "color:pink"
        } else if lexer::is_integer(token) {
            "color:red"
        } else if lexer::is_punct(token) {
            "color:blue"
        } else if self.keywords.contains_key(token.literal_ref()) {
            "color:purple"
        } else {
            "color:black"
        };

        let span = document().create_element("span").unwrap();
        span.set_attribute("style", color).unwrap();
        span.class_list().add_1("highlight").unwrap();
        span.append_child(&document().create_text_node(token.literal_ref()))
            .unwrap();
        parent_node.insert_before(&span, Some(text_node)).unwrap();
        log!(format!(
            "high_light_word: {:}",
            parent_node.dyn_ref::<HtmlElement>().unwrap().inner_html()
        ));
    }

    fn insert_text_string(text_node: &Node, range: Range<usize>) {
        let code = text_node.node_value().unwrap();
        let code = Self::change_nbsp_to_space(&code);
        let parent_node = text_node.parent_node().unwrap();
        let str_before = &code[range];
        if !str_before.is_empty() {
            let before = Self::change_space_to_nbsp(str_before);
            let text_node_new = document().create_text_node(&before);
            parent_node
                .insert_before(&text_node_new, Some(text_node))
                .unwrap();
        }
    }

    /// 转换空格
    pub fn change_space_to_nbsp(str: &str) -> String {
        let mut ret = String::default();
        for ch in str.chars() {
            if ch == ' ' {
                ret.push('\u{00a0}');
            } else {
                ret.push(ch);
            }
        }
        ret
    }

    pub fn change_nbsp_to_space(str: &str) -> String {
        let mut ret = String::default();
        for ch in str.chars() {
            if ch == '\u{00a0}' {
                ret.push(' ');
            } else {
                ret.push(ch);
            }
        }
        ret
    }

    fn keyup(&mut self, key: &str) -> Result<()> {
        if key == " "
            || key == "Enter"
            || key == "ArrowRight"
            || key == "ArrowLeft"
            || key == "ArrowUp"
            || key == "ArrowDown"
            || key == "Shift"
        {
            return Ok(());
        }

        // 一定有光标
        let selection = window().get_selection().unwrap().unwrap();
        // 寻找光标所在的行
        let mut line_div = Self::find_line_by_cursor(&selection.anchor_node().unwrap())
            .ok_or(Error::from("find_line_by_cursor: 没有找到光标所在的node"))?;

        // 判断是不是当前的div节点
        if line_div == self.node_ref.get().unwrap() {
            //是当前的div节点，那么创建一个行
            log!("和当前的节点一致");
            if &selection.anchor_node().unwrap().node_name() == "DIV" {
                return Ok(());
            }
            line_div = self
                .current_div_node_handle()
                .ok_or(Error::from("当前节点生成div失败"))?;
            return Ok(());
        }
        line_div
            .dyn_ref::<HtmlElement>()
            .unwrap()
            .set_attribute("contenteditable", "true")
            .unwrap();
        let mut tmp_string = String::default();
        let index = Self::selection_to_index(&selection, &line_div, &mut tmp_string);
        let index = if let Some(index) = index { index } else { 0 };

        let line_childs = line_div.child_nodes();
        for index in 0..line_childs.length() {
            let ch = line_childs.get(index).unwrap();
            if &ch.node_name() == "SPAN" {
                let content_span = ch.text_content().unwrap();
                let text_node = document().create_text_node(&content_span);
                line_div.insert_before(&text_node, Some(&ch)).unwrap();
                line_div.remove_child(&ch).unwrap();
            }
        }

        // 把所有相邻的text节点合并
        line_div.normalize();
        // 合并到这里，可以看做是一个<div> <text>code</text> </div>
        // 调用词法解析器进行解析
        Self::change_node(Edit::new(self), line_div.clone());
        // 解析完毕之后，进行高亮显示
        self.high_light_syntax()?;
        // 恢复光标
        tmp_string = String::default();
        let node = Self::selection_to_node(index as u32, &line_div, &mut tmp_string);
        let node = if let Some(node) = node {
            node
        } else {
            (line_div, 0u32)
        };

        selection
            .set_base_and_extent(&node.0, node.1, &node.0, node.1)
            .map_err(|e| Error::from(format!("{:?}", e)))?;
        Ok(())
    }

    // 把div节点下的所有text内容拼接起来，寻找定位的index
    fn selection_to_index(
        selection: &Selection,
        source_node: &Node,
        str: &mut String,
    ) -> Option<usize> {
        // 把selection统一转成source_code下面的text的index
        let childs = source_node.child_nodes();
        for i in 0..childs.length() {
            let child = childs.get(i).unwrap();
            if &child.node_name() == "BR" {
                continue;
            }
            let ret = Self::selection_to_index(selection, &child, str);
            if ret.is_none() {
                continue;
            } else {
                return ret;
            }
        }
        if let Some(code) = source_node.node_value() {
            log!(format!("code: {}", code));
            let anchor_node = selection.anchor_node().unwrap();
            let anchor_offset = selection.anchor_offset();
            if &anchor_node == source_node {
                return Some(str.len() + anchor_offset as usize);
            }
            str.push_str(&Self::text_string(source_node));
        }
        None
    }

    // 根据所有的text内容的index偏移，寻找到节点内的偏移
    fn selection_to_node(index: u32, node: &Node, str: &mut String) -> Option<(Node, u32)> {
        log!(format!("selection_to_node: index: {}", index));
        let childs = node.child_nodes();
        for i in 0..childs.length() {
            let child = childs.get(i).unwrap();
            let ret = Self::selection_to_node(index, &child, str);
            if ret.is_none() {
                continue;
            } else {
                return ret;
            }
        }
        if let Some(_) = node.node_value() {
            let current_str = Self::text_string(node);
            log!(format!(
                "selection_to_node: {}  str:{}  str_len:{} str.len() + current_str.len(): {}",
                current_str,
                str,
                str.len(),
                str.len() + current_str.len()
            ));
            if current_str.len() >= index as usize {
                return Some((node.clone(), index));
            }
            if str.len() + current_str.len() >= index as usize {
                // 找到当前的节点
                return Some((node.clone(), index - str.len() as u32));
            }
            str.push_str(&current_str);
        }
        None
    }

    fn text_string(node: &Node) -> String {
        let code = node.node_value().unwrap();
        Self::change_nbsp_to_space(&code)
    }

    // 查找光标所在的div节点，也就是一行, 光标总是在一个Text节点
    fn find_line_by_cursor(anchor_node: &Node) -> Option<Node> {
        if &anchor_node.node_name() == "DIV" {
            return Some(anchor_node.clone());
        }
        let mut parent = anchor_node.parent_node();
        while let Some(p) = parent {
            if &p.node_name() == "DIV" {
                return Some(p);
            }
            parent = p.parent_node();
        }
        None
    }

    fn create_node(str: &str) -> Option<Node> {
        document().create_element(str).ok()?.dyn_into::<Node>().ok()
    }

    // 当前节点的处理
    fn current_div_node_handle(&self) -> Option<Node> {
        let current_div = self.node_ref.get()?;
        let selection = window().get_selection().ok()??;
        let anchor_node = selection.anchor_node()?;
        log!(format!("anchor_node: {:?}", anchor_node));
        let mut tmp = String::default();
        let global_index = Self::selection_to_index(&selection, &current_div, &mut tmp)?;
        let content = current_div.text_content()?;
        let new_div = Self::create_node("div")?;
        new_div
            .dyn_ref::<HtmlElement>()
            .unwrap()
            .set_attribute("contenteditable", "true")
            .unwrap();
        new_div
            .append_child(&document().create_text_node(&content))
            .ok()
            .unwrap();
        if anchor_node.node_name() == "FONT" {
            current_div
                .insert_before(&new_div, anchor_node.parent_node().as_ref())
                .unwrap();
        } else {
            current_div
                .insert_before(&new_div, Some(&anchor_node))
                .unwrap();
        }

        current_div.remove_child(&anchor_node).ok();
        // 恢复光标位置
        let node = Self::selection_to_node(global_index as u32, &new_div, &mut tmp).unwrap();
        selection.collapse_with_offset(Some(&node.0), node.1).ok()?;
        Some(new_div)
    }
}

#[derive(Debug, Clone)]
struct NodeToken {
    div_node: Node,
    token: Token,
}

impl NodeToken {
    fn new(div_node: Node, token: Token) -> Self {
        Self { div_node, token }
    }
}

#[derive(Copy)]
pub struct Edit {
    inner: *mut CompilerEditor,
}

impl Clone for Edit {
    fn clone(&self) -> Self {
        Self { inner: self.inner }
    }
}

impl Debug for Edit {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.inner_ref().fmt(f)
    }
}

impl Edit {
    pub fn new(c: &CompilerEditor) -> Self {
        Self {
            inner: c as *const CompilerEditor as *mut CompilerEditor,
        }
    }

    pub fn inner_mut(&self) -> &mut CompilerEditor {
        unsafe { &mut *(self.inner) }
    }

    pub fn inner_ref(&self) -> &CompilerEditor {
        unsafe { &*(self.inner) }
    }
}

/// 为CompilerEditor实现Observer
impl Observe for Edit {
    type Context = Node;
    fn notify_token_creation(self, tok: Token, ctx: Self::Context) {
        let token_node = NodeToken::new(ctx, tok);
        self.inner_mut().tokens.push(token_node);
        log!(format!("通知: {:?}", self.inner_ref().tokens));
    }
}
